//
// NamedEvent_UNIX.cpp
//
// $Id: //poco/1.4/Foundation/src/NamedEvent_UNIX.cpp#1 $
//
// Library: Foundation
// Package: Processes
// Module:  NamedSemaphore
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/NamedSemaphore_UNIX.h"
#include "Poco/Format.h"
#include "Poco/Exception.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/time.h>
#if defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX)
#include <semaphore.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#endif


namespace Poco {

NamedSemaphoreImpl::NamedSemaphoreImpl(const std::string& name,SEMAPHORE_SHARE_TYPE shareType/* = SEMAPHORE_SHARE_THREAD*/):
	m_name(name)
{
	std::string fileName = getFileName();
#if defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX) || LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	if (!m_name.empty())
	{
		m_sem = sem_open(fileName.c_str(), O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 0);
		if ((long) m_sem == (long) SEM_FAILED) 
			throw SystemException(Poco::format("cannot create named mutex %s (sem_open() failed, errno=%d)", fileName.c_str(), errno), m_name);
	}
	else
	{
		int ret = sem_init(&m_unamedSem, shareType, 0);
		if (ret == EINVAL) 
			throw SystemException(Poco::format("cannot create unnamed mutex (sem_init() failed, errno=%d)", errno), m_name);
		m_sem = &m_unamedSem;
	}
#endif // defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX)
}


NamedSemaphoreImpl::~NamedSemaphoreImpl()
{
#if defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX) || LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	sem_close(m_sem);
	// 不需要在这里调用unlink删除Semaphore，其他进程有可能还在用
#endif
}


void NamedSemaphoreImpl::setImpl()
{
#if defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX) || LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	if (sem_post(m_sem) != 0)
		throw SystemException("cannot set named event", m_name);
#endif
}


void NamedSemaphoreImpl::waitImpl()
{
#if defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX) || LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	int err;
	do
	{
		err = sem_wait(m_sem);
	}
	while (err && errno == EINTR);
	if (err) throw SystemException("cannot wait for named event", m_name);
#endif
}

bool NamedSemaphoreImpl::tryWaitImpl()
{
#if defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX) || LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	int err;
	err = sem_trywait(m_sem);
	return err == 0;
#endif
	return false;
}

bool NamedSemaphoreImpl::tryWaitImpl(long milliseconds)
{
#if defined(sun) || defined(__APPLE__) || defined(__osf__) || defined(__QNX__) || defined(_AIX) || LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	int err;
	struct timespec abstime;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    abstime.tv_sec  = tv.tv_sec + milliseconds / 1000;
    abstime.tv_nsec = tv.tv_usec*1000 + (milliseconds % 1000)*1000000;
    if (abstime.tv_nsec >= 1000000000)
    {
        abstime.tv_nsec -= 1000000000;
        abstime.tv_sec++;
    }
    
	err = sem_timedwait(m_sem,&abstime);
	return err == 0;
#endif
	return false;
}

std::string NamedSemaphoreImpl::getFileName()
{
#if defined(sun) || defined(__APPLE__) || defined(__QNX__) || LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
	std::string fn = "/";
#else
	std::string fn = "/tmp/";
#endif
	fn.append(m_name);
	fn.append(".event");
	return fn;
}


} // namespace Poco
